﻿// Found the IndexedDictionary source code online at: http://weblogs.asp.net/bradvincent/archive/2007/12/13/220-berutils-part-4-collections.aspx

using System;
using System.Collections.Generic;
using System.Text;

namespace ElveTouch
{
    /// <summary>
    /// An indexable Dictionary
    /// </summary>
    /// <typeparam name="TKey">type for the key</typeparam>
    /// <typeparam name="TValue">type for the value</typeparam>
    public class IndexedDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
        protected List<TKey> m_col = new List<TKey>();
        protected bool m_bReplaceDuplicateKeys = false; //can key be inserted more than once?
        //if so then it will if it contains the key on add

        protected bool m_bThrowErrorOnInvalidRemove = false; //if true then throws an exception if the 
        //item you are trying to remove does not exist in the collection

        #region Contructors
        public IndexedDictionary()
        { }

        public IndexedDictionary(bool bReplaceDuplicateKeys)
        {
            m_bReplaceDuplicateKeys = bReplaceDuplicateKeys;
        }

        public IndexedDictionary(bool bReplaceDuplicateKeys, bool bThrowErrorOnInvalidRemove)
            : this(bReplaceDuplicateKeys)
        {
            m_bThrowErrorOnInvalidRemove = bThrowErrorOnInvalidRemove;
        }
        #endregion

        public bool ReplaceDuplicateKeys
        {
            get { return m_bReplaceDuplicateKeys; }
        }

        public bool ThrowErrorOnInvalidRemove
        {
            get { return m_bThrowErrorOnInvalidRemove; }
        }
        //useful for changing the key in subclasses.
        protected virtual TKey TransformKey(TKey key)
        {
            return key;
        }

        public bool Contains(TKey key)
        {
            return base.ContainsKey(TransformKey(key));
        }

        public virtual new void Add(TKey key, TValue item)
        {
            AddAt(-1, key, item);
        }

        public virtual void AddAt(int index, TKey key, TValue item)
        {
            if (m_bReplaceDuplicateKeys && ContainsKey(key))   //check if it contains and remove
                Remove(key);

            base.Add(TransformKey(key), item);

            if (index != -1)
                m_col.Insert(index, key);
            else
                m_col.Add(key);
        }

        public virtual TValue RemoveAt(int index)
        {
            if (m_bThrowErrorOnInvalidRemove)
                if (index < 0 || index >= m_col.Count)
                throw new Exception("Cannot remove invalid Index");
            TKey key = m_col[index];
            return Remove(key);
        }

        public new TValue Remove(TKey key)
        {
            bool bContains = ContainsKey(key);
            if (m_bThrowErrorOnInvalidRemove && !bContains)
                throw new Exception("Key does not exist within the Dictionary");
            else if (!bContains)
                return default(TValue);
            TValue value = base[TransformKey(key)];
            m_col.Remove(TransformKey(key));
            base.Remove(TransformKey(key));
            return value;
        }

        public new bool ContainsKey(TKey key)
        {
            return base.ContainsKey(TransformKey(key));
        }

        #region Indexers
        public new TValue this[TKey key]
        {
            get
            {
                return base[TransformKey(key)];
            }
            set
            {
                base[TransformKey(key)] = value;
            }
        }


        public TValue this[int index]
        {
            get
            {
                return this[m_col[index]];
            }
            set
            {
                this[m_col[index]] = value;
            }
        }
        #endregion

        /// <summary>
        /// Searches for the specified object and returns the zero-based index of the
        /// first occurrence within the entire System.Collections.Generic.List&lt;T&gt;.
        /// </summary>
        /// <param name="key">The object to locate in the System.Collections.Generic.List&lt;T&gt;. The value can be null for reference types.</param>
        /// <returns> The zero-based index of the first occurrence of item within the entire System.Collections.Generic.List&lt;T&gt;, if found; otherwise, –1.</returns>
        public int IndexOfKey(TKey key)
        {
            return m_col.IndexOf(key);
        }

        public TKey GetKeyAt(int index)
        {
            return m_col[index];
        }
    }
}
